package com.photoeditor.demo.util;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class KeyBoardHelper {

	static class HideRunnable implements Runnable {

		private WeakReference<EditText> mEditTextWRF;
		private InputMethodManager mInputMethodManager;

		public HideRunnable(InputMethodManager inputMethodManager, EditText editTextWRF) {
			mInputMethodManager = inputMethodManager;
			mEditTextWRF = new WeakReference<>(editTextWRF);
		}

		@Override
		public void run() {
			if(null != mEditTextWRF.get()) {
				mInputMethodManager.hideSoftInputFromWindow(mEditTextWRF.get().getWindowToken(), 0);
			}
		}
	}

	static class ShowRunnable implements Runnable {

		private WeakReference<EditText> mEditTextWRF;
		private InputMethodManager mInputMethodManager;

		public ShowRunnable(InputMethodManager inputMethodManager, EditText editTextWRF) {
			mInputMethodManager = inputMethodManager;
			mEditTextWRF = new WeakReference<>(editTextWRF);
		}

		@Override
		public void run() {
			if(null != mEditTextWRF.get()) {
				mInputMethodManager.showSoftInput(mEditTextWRF.get(), 0);
			}
		}
	}

	/**
     * 关闭虚拟键盘
     * @param aContext  程序上下文
     * @param aEdiText  当前对应输入框 会出现内存泄漏
     */
	public static void hideKeyBoard(Context aContext, EditText aEdiText) {
		if (null == aEdiText || null == aContext) {
			return;
		}
		InputMethodManager imm = (InputMethodManager) aContext
				.getSystemService(Context.INPUT_METHOD_SERVICE);
		if (aEdiText != null) {
			com.common.base.utils.thread.ThreadPool.runUITask(new HideRunnable(imm, aEdiText));
		}
	}

	/**
	 * 立即关闭虚拟键盘
	 * @param aContext  程序上下文
	 * @param aEdiText  当前对应输入框
	 */
	public static void hideKeyBoardNotPost(Context aContext,
									final EditText aEdiText) {
		if (null == aEdiText || null == aContext) {
			return;
		}
		final InputMethodManager imm = (InputMethodManager) aContext
				.getSystemService(Context.INPUT_METHOD_SERVICE);
		if (aEdiText != null) {
			imm.hideSoftInputFromWindow(aEdiText.getWindowToken(), 0);
		}
	}

    /**
     * 打开虚拟键盘
     * @param aContext  程序上下文
     * @param aEdiText  当前对应输入框
     */
	public static void showKeyBoard(Context aContext,
			final EditText aEdiText) {
		if (null == aEdiText || null == aContext) {
			return;
		}
		final InputMethodManager imm = (InputMethodManager) aContext
				.getSystemService(Context.INPUT_METHOD_SERVICE);
		if (aEdiText != null) {
			aEdiText.requestFocus();
			aEdiText.postDelayed(new Runnable() {

				@Override
				public void run() {
					imm.showSoftInput(aEdiText, 0);
				}
			}, 250);
		}
	}
	/**
     * 无延迟打开虚拟键盘
     * @param aContext  程序上下文
     * @param aEdiText  当前对应输入框
     */
	public static void showKeyBoardWithoutDelay(Context aContext, EditText aEdiText) {
		if (null == aEdiText || null == aContext) {
			return;
		}
		InputMethodManager imm = (InputMethodManager) aContext
				.getSystemService(Context.INPUT_METHOD_SERVICE);
		if (aEdiText != null) {
			com.common.base.utils.thread.ThreadPool.runUITask(new ShowRunnable(imm, aEdiText));
		}
	}

	public static void fixFocusedViewLeak(Application application) {

		// Don't know about other versions yet.
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1|| Build.VERSION.SDK_INT > 23) {
			return;
		}

		final InputMethodManager inputMethodManager =
				(InputMethodManager) application.getSystemService(Context.INPUT_METHOD_SERVICE);

		final Field mServedViewField;
		final Field mHField;
		final Method finishInputLockedMethod;
		final Method focusInMethod;
		try {
			mServedViewField = InputMethodManager.class.getDeclaredField("mServedView");
			mServedViewField.setAccessible(true);
			mHField = InputMethodManager.class.getDeclaredField("mServedView");
			mHField.setAccessible(true);
			finishInputLockedMethod = InputMethodManager.class.getDeclaredMethod("finishInputLocked");
			finishInputLockedMethod.setAccessible(true);
			focusInMethod = InputMethodManager.class.getDeclaredMethod("focusIn", View.class);
			focusInMethod.setAccessible(true);
		} catch (NoSuchMethodException | NoSuchFieldException unexpected) {
			Log.e("IMMLeaks", "Unexpected reflection exception", unexpected);
			return;
		}

		application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
			@Override
			public void onActivityDestroyed(Activity activity){

			}

			@Override
			public void onActivityStarted(Activity activity){

			}

			@Override
			public void onActivityResumed(Activity activity){

			}

			@Override
			public void onActivityPaused(Activity activity){

			}

			@Override
			public void onActivityStopped(Activity activity){

			}

			@Override
			public void onActivitySaveInstanceState(Activity activity, Bundle bundle){

			}

			@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
				ReferenceCleaner cleaner = new ReferenceCleaner(inputMethodManager, mHField, mServedViewField,
						finishInputLockedMethod);
				View rootView = activity.getWindow().getDecorView().getRootView();
				ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver();
				viewTreeObserver.addOnGlobalFocusChangeListener(cleaner);
			}
		});
	}

	static class ReferenceCleaner
			implements MessageQueue.IdleHandler, View.OnAttachStateChangeListener,
			ViewTreeObserver.OnGlobalFocusChangeListener {

		private final InputMethodManager inputMethodManager;
		private final Field mHField;
		private final Field mServedViewField;
		private final Method finishInputLockedMethod;

		ReferenceCleaner(InputMethodManager inputMethodManager, Field mHField, Field mServedViewField,
						 Method finishInputLockedMethod) {
			this.inputMethodManager = inputMethodManager;
			this.mHField = mHField;
			this.mServedViewField = mServedViewField;
			this.finishInputLockedMethod = finishInputLockedMethod;
		}

		@Override public void onGlobalFocusChanged(View oldFocus, View newFocus) {
			if (newFocus == null) {
				return;
			}
			if (oldFocus != null) {
				oldFocus.removeOnAttachStateChangeListener(this);
			}
			Looper.myQueue().removeIdleHandler(this);
			newFocus.addOnAttachStateChangeListener(this);
		}

		@Override public void onViewAttachedToWindow(View v) {
		}

		@Override public void onViewDetachedFromWindow(View v) {
			v.removeOnAttachStateChangeListener(this);
			Looper.myQueue().removeIdleHandler(this);
			Looper.myQueue().addIdleHandler(this);
		}

		@Override public boolean queueIdle() {
			clearInputMethodManagerLeak();
			return false;
		}

		private void clearInputMethodManagerLeak() {
			try {
				Object lock = mHField.get(inputMethodManager);
				// This is highly dependent on the InputMethodManager implementation.
				synchronized (lock) {
					View servedView = (View) mServedViewField.get(inputMethodManager);
					if (servedView != null) {

						boolean servedViewAttached = servedView.getWindowVisibility() != View.GONE;

						if (servedViewAttached) {
							// The view held by the IMM was replaced without a global focus change. Let's make
							// sure we get notified when that view detaches.

							// Avoid double registration.
							servedView.removeOnAttachStateChangeListener(this);
							servedView.addOnAttachStateChangeListener(this);
						} else {
							// servedView is not attached. InputMethodManager is being stupid!
							Activity activity = extractActivity(servedView.getContext());
							if (activity == null || activity.getWindow() == null) {
								// Unlikely case. Let's finish the input anyways.
								finishInputLockedMethod.invoke(inputMethodManager);
							} else {
								View decorView = activity.getWindow().peekDecorView();
								boolean windowAttached = decorView.getWindowVisibility() != View.GONE;
								if (!windowAttached) {
									finishInputLockedMethod.invoke(inputMethodManager);
								} else {
									decorView.requestFocusFromTouch();
								}
							}
						}
					}
				}
			} catch (IllegalAccessException |InvocationTargetException unexpected) {
				Log.e("IMMLeaks", "Unexpected reflection exception", unexpected);
			}
		}

		private Activity extractActivity(Context context) {
			while (true) {
				if (context instanceof Application) {
					return null;
				} else if (context instanceof Activity) {
					return (Activity) context;
				} else if (context instanceof ContextWrapper) {
					Context baseContext = ((ContextWrapper) context).getBaseContext();
					// Prevent Stack Overflow.
					if (baseContext == context) {
						return null;
					}
					context = baseContext;
				} else {
					return null;
				}
			}
		}
	}

}
